package com.youlin.untils;

import com.youlin.dto.Status;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

public class RedisUtil {

    private static final long EXPIRELONG = 15;
    private static final TimeUnit TIMEUNIT = TimeUnit.MINUTES;

    /**
     * 向缓存中添加非时效数据
     * @param redisTemplate
     * @param key
     * @param obj
     * @return
     */
    public static void setObject (RedisTemplate redisTemplate, String key, Object obj) {
        redisTemplate.opsForValue().set(key, obj);
    }

    /**
     * 向缓存中添加时效数据
     * @param redisTemplate
     * @param key
     * @param obj
     * @return
     */
    public static void setObjectExpire (RedisTemplate redisTemplate, String key, Object obj) {
        redisTemplate.opsForValue().set(key, obj, EXPIRELONG, TIMEUNIT);
    }

    /**
     * 向缓存中添加时效数据
     * @param redisTemplate
     * @param key
     * @param obj
     * @return
     */
    public static void setObjectExpire (RedisTemplate redisTemplate, String key, Object obj, long EXPIRELONG, TimeUnit TIMEUNIT) {
        redisTemplate.opsForValue().set(key, obj, EXPIRELONG, TIMEUNIT);
    }

    public static <T> T getObject (RedisTemplate redisTemplate, String key, Class<T> clazz) {
        return (T) redisTemplate.opsForValue().get(key);
    }

    public static <T> List<T> getList (RedisTemplate redisTemplate, String key, Class<T> clazz) {
        return (List<T>) redisTemplate.opsForValue().get(key);
    }

    public static void addExpire (RedisTemplate redisTemplate, String key) {
        redisTemplate.expire(key, EXPIRELONG, TIMEUNIT);
    }

    public static <T> RedisSupplier<T> getObjectLocked (RedisTemplate redisTemplate, String key,
                                Class<T> clazz, Object lock, Supplier<RedisSupplier<T>> supplier) {
        T t = getObject(redisTemplate, key, clazz);
        boolean predicate;
        if (clazz != String.class) predicate = t == null;
        else predicate = StringUtils.isBlank((String) t);
        if (predicate) {
            synchronized (lock) {
                t = getObject(redisTemplate, key, clazz);
                if (clazz != String.class) predicate = t == null;
                else predicate = StringUtils.isBlank((String) t);
                if (predicate) {
                    RedisSupplier<T> redisSupplier = supplier.get();
                    RedisUtil.setObjectExpire(redisTemplate, key, redisSupplier.getRedisValue());
                    return redisSupplier;
                }
            }
        }
        return new RedisSupplier<>(t, Status.SUCCESS);
    }

    @NoArgsConstructor
    @Data
    @Accessors(chain=true)
    public static class RedisSupplier<T> {

        private T redisValue;

        private Status status;

        public RedisSupplier(T redisValue, Status status) {
            this.redisValue = redisValue;
            this.status = status;
        }

        public boolean isSuccess () {
            return this.status == Status.SUCCESS;
        }

    }

}
